#ifndef GAME_ANIMATION_MAKE_HEIGHTMAP_H
#define GAME_ANIMATION_MAKE_HEIGHTMAP_H
//
// Game Animation.
// Copyright (C) 2007 Department of Computer Science, University of Copenhagen
//
#include <animation/configuration.h>

namespace animation
{

  /**
  * Auxiliary Method used by the function make_heightmap().
  *
  * NOTE: We call the z-coordinate value the height of the heightmap.
  *
  * @param i        The i'th grid node index.
  * @param j        The j'th grid node index.
  * @param dx       Size of heightmap cell along x-axis.
  * @param dy       Size of heightmap cell along y-axis.
  * @param min_x    The minimum x-coordinate of the heightmap
  * @param min_y    The minimum y-coordinate of the heightmap
  * @param min_z    The minimum z-coordinate of the heightmap
  * @param range_z  The allowable z-range for heightmap points.
  * @param noise    The function used to determin the height of the heightmap node.
  * @param coord    The resulting position of the (i,j)'th heightmap node.
  */
  template<typename vector3_type,typename real_type,typename noise_type>
  inline void  compute_coord(
      int i
    , int j
    , real_type dx
    , real_type dy
    , real_type min_x
    , real_type min_y
    , real_type min_z
    , real_type range_z
    , noise_type & noise
    , vector3_type & coord
    )
  {
    coord(0) = i*dx + min_x;
    coord(1) = j*dy + min_y;
	//This line does the same as the out commented line below.
	coord(2) = range_z*(noise(coord(0)*.25,coord(1)*0.25)*0.5 + 0.5+(noise(coord(0)*0.05,coord(1)*0.05)*4))+  min_z;
	/*
    real_type factor  =noise(coord(0)*.25,coord(1)*0.25)*0.5 + 0.5;
    coord(2) = range_z*factor +  min_z;

    //--- hmm, let make some large hills and valies as well:-)
    factor  = noise(coord(0)*0.05,coord(1)*0.05);
    coord(2) += 4*range_z*factor;
	*/
  }

  /**
  * Make Height Map Geometry.
  * This function uses a noise function to create a smooth bumpy height field.
  *
  * NOTE: We call the z-coordinate value the height of the heightmap.
  *
  * @param I           Number of height cells along x-axis
  * @param J           Number of height cells along y-axis
  * @param min_coord   The minimum coordinates which the heightmap should fit into.
  * @param max_coord   The maximum coordinates which the heightmap should fit into.
  * @param noise       The function used to compute the height-values at specific points.
  * @param geometry    Upon return this argument holds the resulting geometry of the heightmap.
  */
  template<typename vector3_type,typename geometry_type,typename noise_type>
    inline void make_heightmap(
      int const & I
    , int const & J
    , vector3_type const & min_coord
    , vector3_type const & max_coord
    , noise_type & noise
    , geometry_type & geometry
    )
  {
    typedef typename vector3_type::value_type    real_type;
    typedef typename geometry_type::vertex_type  vertex_type;

    assert(I>=1 || !"make_height_map_geometry(): I must less than one");
    assert(J>=1 || !"make_height_map_geometry(): J must less than one");

    int M = I+1;  //--- number of vertices along x-axis
    int N = J+1;  //--- number of vertices along y-axis

    unsigned int vertices = M*N;
    unsigned int indices  = I*J*6;
    if(geometry.vertices() != vertices)
      geometry = geometry_type(vertices,indices);

    vector3_type range = max_coord - min_coord;
    real_type dx = range(0) / I;   //--- width of height cell
    real_type dy = range(1) / J;   //--- depth of height cell

    real_type  repetition_factor = 3.0;

    real_type xscale =   repetition_factor*dx/range(0);
    real_type yscale =   repetition_factor*dy/range(1);

    int k = 0;
    for(int j=0;j<J;++j)
    {
	  int jM=j*M;
	  int j1M=jM+M;
      for(int i=0;i<I;++i)
      {
        geometry.get_index(k++) =   jM + i;
        geometry.get_index(k++) =   jM + i+1;
        geometry.get_index(k++) =  j1M + i+1;
        geometry.get_index(k++) =   jM + i;
        geometry.get_index(k++) =  j1M + i+1;
        geometry.get_index(k++) =  j1M + i;
      }
    }
    for(int j=0;j<N;++j)
    {
      for(int i=0;i<M;++i)
      {
        vertex_type & vertex = geometry.get_vertex(j*M + i);
        compute_coord(i,j,dx,dy,min_coord(0),min_coord(1),min_coord(2),range(2),noise,vertex.m_coord);
        vertex.m_tex0[0]  = i*xscale;
        vertex.m_tex0[1]  = j*yscale;
      }
    }
    //--- Angle weighted pseudo normals
    vector3_type v1,v2,v3,v4,v5,v6;
	int index=0;
    for( int j=0;j<N;++j)
    {
      for( int i=0;i<M;++i)
      {
        //   +--3--2--+  j+1
        //   | /| /| /|
        //   |/ |/ |/ |
        //   4--0--1--+  j
        //   | /| /| /|
        //   |/ |/ |/ |
        //   5--6--+--+  j-1
        //      i
        vertex_type & v0 = geometry.get_vertex(index + (i  ) );
		//As long as we are not on the outskirts of the grid we can use the coords we already computed.
		//This could still be optimised cause even if we are on the outskirts we could still use some of
		//the precomputed coords.
		if(j==0||j==N-1||i==0||i==M-1){
        compute_coord( i+1, j  , dx, dy, min_coord(0), min_coord(1), min_coord(2), range(2), noise, v1);
        compute_coord( i+1, j+1, dx, dy, min_coord(0), min_coord(1), min_coord(2), range(2), noise, v2);
        compute_coord( i  , j+1, dx, dy, min_coord(0), min_coord(1), min_coord(2), range(2), noise, v3);
        compute_coord( i-1, j  , dx, dy, min_coord(0), min_coord(1), min_coord(2), range(2), noise, v4);
        compute_coord( i-1, j-1, dx, dy, min_coord(0), min_coord(1), min_coord(2), range(2), noise, v5);
        compute_coord( i  , j-1, dx, dy, min_coord(0), min_coord(1), min_coord(2), range(2), noise, v6);
		}else{
        //--- We could have looked up some of the vertices instead of recomputing them:
        v1 = geometry.get_vertex( (j  )*M + (i+1) ).m_coord;
        v2 = geometry.get_vertex( (j+1)*M + (i+1) ).m_coord;
        v3 = geometry.get_vertex( (j+1)*M + (i  ) ).m_coord;
        v4 = geometry.get_vertex( (j  )*M + (i-1) ).m_coord;
        v5 = geometry.get_vertex( (j-1)*M + (i-1) ).m_coord;
        v6 = geometry.get_vertex( (j-1)*M + (i  ) ).m_coord;
		}
        vector3_type v10 = unit( v1 -  v0.m_coord );
        vector3_type v20 = unit( v2 -  v0.m_coord );
        vector3_type v30 = unit( v3 -  v0.m_coord );
        vector3_type v40 = unit( v4 -  v0.m_coord );
        vector3_type v50 = unit( v5 -  v0.m_coord );
        vector3_type v60 = unit( v6 -  v0.m_coord );
        v0.m_normal = unit( (v10%v20) + (v20%v30) + (v30%v40) + (v40%v50) + (v50%v60) + (v60%v10) );
      }
	  index+=M;
    }
  }

} // namespace animation

// GAME_ANIMATION_MAKE_HEIGHTMAP_H
#endif
